import it.unisa.dia.gas.jpbc.*;
import it.unisa.dia.gas.plaf.jpbc.pairing.PairingFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

public class Arithmetic {

    public static final String rootFloder= "/home/chen/Documents/new_scheme/parent/scheme/Data";
    public static final String fileFloder= "Shen";
    public static final String size= "1MB_file.txt";
    public static final String testDatafilePath = rootFloder + "/" + fileFloder + "/" + size;

    public static Pairing pairing = PairingFactory.getPairing("a.properties");

    public static Element fAdd(Element a, Element b){
        return pairing.getZr().newElement().set(a).add(b);
    }

    public static Element fSub(Element a, Element b){
        return pairing.getZr().newElement().set(a).sub(b);
    }

    public static Element fMul(Element a, Element b){
        return pairing.getZr().newElement().set(a).mul(b);
    }

    public static Element fDiv(Element a, Element b){
        return pairing.getZr().newElement().set(a).div(b);
    }


    public static Element polynomialEval(Element[] p, Element x){
        Element s = pairing.getZr().newElement().setToZero();
        for(int i = 0; i < p.length; i++) {
            Element xi = pairing.getZr().newElement().set(x).pow(BigInteger.valueOf(i));
            Element elem = fMul(xi, p[i]);
            s = fAdd(s, elem);
        }
        return s;
    }

    public static Element pbcPolynomialiEval(Element[][] Mx, Element x, int i){
        Element r = pairing.getZr().newElement().setToZero();
        for(int j = 0; j < Mx[i].length; j++){
            Element xi = x.pow(BigInteger.valueOf(j));
            Element elem = fMul(xi, Mx[i][j]);
            r = fAdd(r, elem);
        }
        return r;
    }

    //获取随机数
    public static int[] getUniqueRandomNum(int sum, int choice){
        List<Integer> selected = new ArrayList<>();
        for(int i = 0; i < sum; i++){
            selected.add(i);
        }
        long seed = System.nanoTime();
        Collections.shuffle(selected, new Random(seed));
        if(choice < selected.size()){
            selected = selected.subList(0,choice);
        }
        Collections.sort(selected);
        int[] result = new int[selected.size()];
        for(int i = 0; i < selected.size(); i++){
            result[i] = selected.get(i);
        }
        return result;
    }

    public static Element[] polynomialSub(Element[] a, Element[] b){
        int maxLen = a.length;
        if(b.length > maxLen){
            maxLen = b.length;
        }
        Element[] r = new Element[maxLen];
        for(int i = 0; i < r.length; i++){
            r[i] = pairing.getZr().newElement().setToZero();
        }
        for(int i = 0; i < a.length; i++){
            r[i] = fAdd(r[i], a[i]);
        }
        for(int i = 0; i < b.length; i++){
            r[i] = fSub(r[i], b[i]);
        }
        return r;
    }

    //多项式乘法
    public static Element[] polynomialMul(Element[] a, Element[] b){
        Element[] r = new Element[a.length + b.length - 1];
        for(int i = 0; i < r.length; i++){
            r[i] = pairing.getZr().newElement().setToZero();
        }
        for(int i = 0; i < a.length; i++){
            for(int j = 0; j < b.length; j++){
                Element temp = fMul(a[i], b[j]);
                r[i+j] = fAdd(r[i+j], temp);
            }
        }
        return r;
    }

    //输入文件分块，chunk是大块，block是小块，chunk块包含多个block块
    public static byte[][][] hashFilePolyGen(int chunkNum, int tasknum, int blockNum){
        byte[][][] Mx= new byte[chunkNum][blockNum][];
        int blocksize = blockNum * init.bitNum;
        try{
            File file = new File(testDatafilePath);
            FileInputStream fis = new FileInputStream(file);
            for(int i = 0; i < chunkNum; i++){
                fis.getChannel().position(tasknum + i * blocksize);
                byte[] blockBytes = new byte[blocksize];
                fis.read(blockBytes);
                for(int j = 0; j < blockNum; j++){
                    int startIdx = init.bitNum * j;
                    int endIdx = init.bitNum * (j+1);
                    if(endIdx <= blockBytes.length){
                        byte[] temp = new byte[init.bitNum];
                        System.arraycopy(blockBytes, startIdx,temp, 0, init.bitNum);
                        Mx[i][j] = temp;
                    } else {
                        Mx[i][j] = new byte[0];
                    }
                }
            }
        } catch (IOException e){
            e.printStackTrace();
        }
        return Mx;
    }
}
